package org.wheel.plugins.starter.jdbc.pool.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.wheel.module.core.annon.Mapper;
import org.wheel.plugins.starter.jdbc.pool.dao.IBaseDao;

import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 自定义类路径扫描
 * 只扫描 有 {@code org.wheel.module.core.annon.Mapper} 注解以及继承了{@code org.wheel.plugins.starter.jdbc.pool.dao.IBaseDao} 的接口
 */
public class CustomClassPathScanningCandidateComponentProvider extends ClassPathBeanDefinitionScanner {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();

    private String startAppPackages;

    public CustomClassPathScanningCandidateComponentProvider(BeanDefinitionRegistry registry) {
        super(registry, false);
        List<String> baseAppPackages = AutoConfigurationPackages.get((BeanFactory) registry);
        if(CollUtil.isNotEmpty(baseAppPackages)){
            this.startAppPackages = baseAppPackages.get(0);
        }
    }

    public void registerDefaultFilters() {
        // 添加接口过滤器 有Mapper 注解的接口，适合独立接口，没有任何继承类的
        addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
        // 添加 继承了 IBaseDao 接口过滤器
        addBaseDaoFilter();

        // 添加接口过滤器, 接口本身不包含 泛型
//        addIncludeFilter((metadataReader, mf) -> {
//            boolean result = metadataReader.getClassMetadata().isInterface();
//            String className = metadataReader.getClassMetadata().getClassName();
//            try {
//                if(result) {
//                    if(!className.startsWith(startAppPackages)){
//                        return false;
//                    }
//                    Class<?> interfaceClass = ClassUtils.forName(className, null);
//                    // 接口中没有泛型参数，没有继承 BaseService， BaseServiceImpl
//                    return result && !checkInterfaceForGenerics(interfaceClass);
//                }
//            } catch (ClassNotFoundException e) {
////                e.printStackTrace();
////                return false;
//                log.error("无法加载类：" + metadataReader.getClassMetadata().getClassName(), e);
//            }
//            return result;
//        });
    }

//    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
//        return super.findCandidateComponents(basePackage);
//    }

    public void addIncludeFilter(TypeFilter filter) {
        super.addIncludeFilter(filter);
    }

    // 关键判断，满足以下条件则返回 true，就可以添加到容器中
    /*
     * 1. metadata.isIndependent():
     * 这个方法检查类是否没有非 final 的枚举类型或内部类。简单来说，它确保类可以被实例化，并且可以被 Spring 容器管理。
     * 2. metadata.isConcrete():
     * 这个方法检查类是否是具体的（concrete），即它不是抽象的（abstract）并且没有未实现的抽象方法。具体类可以被实例化。
     * 3. metadata.isAbstract():
     * 这个方法检查类是否是抽象的（abstract）。抽象类不能被实例化，但可以有注解方法（annotated methods）。
     * 4. metadata.hasAnnotatedMethods(Lookup.class.getName()):
     * 这个方法检查类是否有被 Lookup 注解标注的方法。Lookup 注解通常用于指定查找方法，这些方法用于在运行时查找其他 Bean。
     */
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        return metadata.isIndependent() && (!metadata.isConcrete() || metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()));
//        return beanDefinition.getMetadata().isInterface() &&
//                beanDefinition.getMetadata().isIndependent();
    }

    // 添加 BaseDao 接口过滤器
    private void addBaseDaoFilter(){
        addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
                return checkInterfaceForImplement(metadataReader.getClassMetadata().getClassName(), IBaseDao.class);
            }
        });
    }

    /**
     * DaoFactory 注册初始化
     * @param basePackages 扫描的包路径
     * @return BeanDefinitionHolder
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        log.trace("开始扫描包：" + StrUtil.join(",", basePackages));
        Set<BeanDefinitionHolder> holders = super.doScan(basePackages);
        for (BeanDefinitionHolder holder : holders) {
            log.trace("扫描到类：" + holder.getBeanName());
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
            // 把接口全限定名传进去
            definition.getConstructorArgumentValues()
                    .addGenericArgumentValue(Objects.requireNonNull(definition.getBeanClassName()));
            // 引用 Spring 容器里的两个单例  DynamicDataSourceProperties， DaoConfigProperties
            definition.getConstructorArgumentValues()
                    .addGenericArgumentValue(new RuntimeBeanReference("dynamicDataSourceProperties"));
            definition.getConstructorArgumentValues()
                    .addGenericArgumentValue(new RuntimeBeanReference("daoConfigProperties"));
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            definition.setBeanClass(DaoFactory.class);

//            definition.setFactoryBeanName(DaoFactory.class.getName());
//            definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//            definition.setFactoryMethodName("getObject");

            log.trace("注册 Bean：" + definition.getBeanClassName());
        }
        return holders;
    }

    /**
     * 检查接口是否继承了某个接口，并返回结果
     * @param className 接口类的名称
     */
    private boolean checkInterfaceForImplement(String className, Class<?> implementsClass) {
        try {
            Class<?> interfaceClass = ClassUtils.forName(className, null);
            boolean result = interfaceClass != null && implementsClass.isAssignableFrom(interfaceClass) && interfaceClass.isInterface() && interfaceClass != implementsClass;

            log.trace("检查接口：" + className + " 是否继承了 " + implementsClass.getName()+" ? is "+result);
            return result;
        } catch (ClassNotFoundException e) {
            log.error("无法加载类：" + className, e);
        }
        return false;
    }

    /**
     * 检查接口是否包含泛型参数
     * @param clazz 接口类
     */
    private boolean checkInterfaceForGenerics(Class<?> clazz) {
        boolean hasGenerics = false;
        // 检查是否是接口
        if (!clazz.isInterface()) {
            System.out.println(clazz.getName() + " is not an interface.");
            return hasGenerics;
        }

        // 获取接口的类型参数
        TypeVariable<? extends Class<?>>[] typeParameters = clazz.getTypeParameters();

        if (typeParameters.length > 0) {
            System.out.println(clazz.getName() + " has " + typeParameters.length + " generic parameters.");
//            for (TypeVariable<? extends Class<?>> typeParameter : typeParameters) {
//                System.out.println("Generic parameter: " + typeParameter.getName());
//            }
            hasGenerics = true;
        } else {
            System.out.println(clazz.getName() + " has no generic parameters.");
        }
        return hasGenerics;
    }
}
